#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <sys/wait.h>
// #include <signal.h>

#include "protocol.hpp"

static const int gnum = 1024; // 全局的 num 变量

namespace Server
{
    using namespace std;

    static const string defaultIp = "0.0.0.0"; // TODO
    static const int gnum = 1024;

    // const Request &req: 输入型
    // Response &resp: 输出型
    typedef std::function<bool(const Request &req, Response &resp)> func_t;
    // typedef function<void(int, string, uint16_t, string)> func_t;

    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        OPEN_ERR
    };

    class udpServer
    {
    public:
        udpServer(const uint16_t &port, const string &ip = defaultIp)
            :_port(port), _ip(ip), _sockfd(-1)
        {
        }
        void initServer()
        {
            // 1. 创建socket
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            cout << "socket success: "
                 << " : " << _sockfd << endl;

            // 2. 绑定port，ip(TODO)
            // 未来服务器要明确的port，不能随意改变
            struct sockaddr_in local; // 定义了一个变量，栈，用户
            bzero(&local, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);                  // 你如果要给别人发消息，你的port和ip要不要发送给对方
            local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. string->uint32_t 2. htonl(); -> inet_addr
            // local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind，服务器的真实写法
            int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
            // UDP Server 的预备工作完成
        }
        
        void start(func_t _callback)
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum];
            for (;;)
            {
                // 1. 读取：req_text: "content_len"\r\n"x op y"\r\n
                // 1.1 你怎么保证你读到的消息是 【一个】完整的请求
                std::string req_text, req_str;

                // 1.2 我们保证，我们req_text里面一定是一个完整的请求："content_len"\r\n"x op y"\r\n
                // 我们得得到 req_str: "x op y"\r\n
                // 因为tcp是字节流的 --> 所以下面的函数保证执行一次，读取出来的 re_text 是一个报文 --> 得不多不少
                // if (!recvPackage(sock, inbuffer, &req_text))
                    // return;
                // 读取数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 必填
                ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                // 1. 数据是什么 2. 谁发的？
                if (s > 0)
                {
                    buffer[s] = 0;
                    string clientip = inet_ntoa(peer.sin_addr); // 1. 网络序列 2. int->点分十进制IP
                    uint16_t clientport = ntohs(peer.sin_port);
                    // string message = buffer;
                    req_text = buffer;

                    // cout << clientip << "[" << clientport << "]# " << message << endl;
                    

                    // std::cout << "带报头的请求：\n"
                    //   << req_text << std::endl;
                    if (!deLength(req_text, &req_str))
                        return;
                    // std::cout << "去掉报头的正文：\n"
                    //   << req_str << std::endl;

                    // 得有一个 Request
                    // 2. 对请求Request，反序列化
                    // 2.1 得到一个结构化的请求对象
                    Request req;
                    if (!req.deserialize(req_str))
                        return;

                    // 3. 计算机处理，req.x, req.op, req.y --- 业务逻辑
                    // 3.1 得到一个结构化的响应
                    Response resp;
                    _callback(req, resp); // req的处理结果，全部放入到了resp， 回调是不是不回来了？不是！

                    // 4.对响应Response，进行序列化
                    // 4.1 得到了一个"字符串"
                    std::string resp_str;
                    resp.serialize(&resp_str);

                    // std::cout << "计算完成, 序列化响应: " << resp_str << std::endl;

                    // 5. 然后我们在发送响应
                    // 5.1 构建成为一个完整的报文
                    std::string send_string = enLength(resp_str); // 添加报头
                    // std::cout << "构建完成完整的响应\n"
                    //   << send_string << std::endl;

                    // 其实这里的发送也是有问题的，不过后面再说
                    // send(sock, send_string.c_str(), send_string.size(), 0);
                    sendto(_sockfd, send_string.c_str(), send_string.size(), 0, (struct sockaddr *)&peer, sizeof(peer));
                }
            }
        }

        ~udpServer()
        {
        }

    private:
        uint16_t _port;
        string _ip; // 实际上，一款网络服务器，不建议指明一个IP
        int _sockfd;
        func_t _callback; // 回调
    };
}
